package com.xayy.health.mgt.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.kawins.base.result.CheckResult;
import cn.kawins.base.result.ResultBody;
import cn.kawins.base.utils.BeanUtil;
import cn.kawins.base.utils.CollUtil;
import cn.kawins.base.utils.StringUtil;
import cn.kawins.mybatis.base.BaseController;
import cn.kawins.mybatis.util.SnowflakeId;
import com.github.pagehelper.PageInfo;
import com.xayy.health.core.config.param.ModelFaultParams;
import com.xayy.health.core.enums.ModelTrainStatusEnum;
import com.xayy.health.core.enums.WebsocketTopicEnum;
import com.xayy.health.mgt.model.ModelTrain;
import com.xayy.health.mgt.model.ModelTrainRecord;
import com.xayy.health.mgt.model.TelemetryData;
import com.xayy.health.mgt.model.dto.DataFeatureDto;
import com.xayy.health.mgt.model.dto.DiagnoseRunDto;
import com.xayy.health.mgt.model.dto.ModelDto;
import com.xayy.health.mgt.model.dto.TelemetryDto;
import com.xayy.health.mgt.model.vo.FailureInfoVo;
import com.xayy.health.mgt.model.vo.PreprocessParamVo;
import com.xayy.health.mgt.model.vo.TaskFaultPredictDataVo;
import com.xayy.health.mgt.model.vo.TaskResultVo;
import com.xayy.health.mgt.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.net.UnknownHostException;
import java.util.*;

/**
 * @author zhangxa
 */
@RestController
@RequestMapping(value = "/diagnose1")
@Api(tags = "事后诊断")
public class DiagnoseController extends BaseController<ModelTrainRecord> {

    @Autowired
    private DiagnoseService diagnoseService;
    @Autowired
    private ModelTrainRecordService modelTrainRecordService;
    @Autowired
    private TaskInfoService taskInfoService;
    @Autowired
    private TaskParamInfoService taskParamInfoService;
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;
    @Autowired
    private MultiParameterService multiParameterService;
    @Autowired
    private SatelliteNodeService satelliteNodeService;


    /**
     * 模型训练
     *
     * @param modelTrain 模型入参
     * @param request    请求
     * @return ResultBody
     */
    @ApiOperation(value = "模型训练")
    @PostMapping("train")
    public ResultBody train(@Valid @RequestBody ModelTrain modelTrain, HttpServletRequest request) {
        // 判断旧模型名称重复
        if (StringUtil.isNotEmpty(modelTrain.getModelName())) {
            CheckResult<Object> checkModelName = modelTrainRecordService.checkModelName(modelTrain);
            if (checkModelName.no()) {
                return CheckResult.fail(checkModelName.getCheckMsg()).body();
            }
        }
        List<String> paramCodes = CollUtil.newArrayList();
        paramCodes.add(modelTrain.getParamCode());
        modelTrain.setParamCodes(paramCodes);
        // 分割故障时间段
        modelTrainRecordService.handlingTimeToFailure(modelTrain);

        modelTrain.setSatelliteMid("");
        Long recordId = new SnowflakeId().genId("tb_model_train_record", "id");
        ModelTrainRecord modelTrainRecord = BeanUtil.copyBean(modelTrain, ModelTrainRecord.class);
        modelTrainRecord.setId(recordId.toString());
        modelTrainRecord.setCreateTime(new Date());
        modelTrainRecord.setStatus(ModelTrainStatusEnum.RUNNING.getCode());

        List<ModelFaultParams> dates = modelTrain.getDates();
        if (CollUtil.isNotEmpty(dates)) {
            StringBuilder errorStart = new StringBuilder();
            StringBuilder errorEnd = new StringBuilder();
            dates.forEach(x -> {
                if (x != null && x.getErrorStart() != null && x.getErrorEnd() != null) {
                    String formatS = DateUtil.format(x.getErrorStart(), "yyyy-MM-dd HH:mm:ss");
                    String formatE = DateUtil.format(x.getErrorEnd(), "yyyy-MM-dd HH:mm:ss");
                    errorStart.append(formatS);
                    errorEnd.append(formatE);
                    errorStart.append(",");
                    errorEnd.append(",");
                }
            });

            if (errorStart.length() > 0 && errorEnd.length() > 0) {
                errorStart.deleteCharAt(errorStart.length() - 1);
                errorEnd.deleteCharAt(errorEnd.length() - 1);
                modelTrainRecord.setErrorStart(errorStart.toString());
                modelTrainRecord.setErrorEnd(errorEnd.toString());
            }

        }

        if (StringUtil.isEmpty(modelTrain.getOldModelName())) {
            modelTrainRecordService.insert(modelTrainRecord);
        } else {
            ModelTrainRecord oldModelTrainRecord = modelTrainRecordService.selectOne(ModelTrainRecord.builder().id(modelTrain.getOldModelName()).build());
            oldModelTrainRecord.setErrorStart(modelTrainRecord.getErrorStart());
            oldModelTrainRecord.setErrorEnd(modelTrainRecord.getErrorEnd());
            modelTrainRecordService.updateByIdSelective(oldModelTrainRecord);
        }

        //模型训练修改为异步调用
        asyncDiagnoseModelTrial(modelTrain, recordId, request);
        return ResultBody.success();
    }

    /**
     * 异步调用模型训练
     *
     * @param modelTrain 入参
     * @param recordId   记录id
     */
    public void asyncDiagnoseModelTrial(ModelTrain modelTrain, Long recordId, HttpServletRequest request) {
        ThreadUtil.execute(() -> {
            String userid = request.getHeader("Userid");
            try {
                ResultBody result = diagnoseService.modelTrial(modelTrain, recordId).body();
                simpMessagingTemplate.convertAndSend(WebsocketTopicEnum.DIAGNOSE.getCode() + "/" + userid, result);
            } catch (Exception e) {
                simpMessagingTemplate.convertAndSend(WebsocketTopicEnum.DIAGNOSE.getCode() + "/" + userid, ResultBody.failed(118, e.getMessage()));
            }
        });
    }


    /**
     * 调用模型
     * @param modelTrain 模型入参
     * @param request 请求
     * @return ResultBody
     */
    @ApiOperation(value = "调用模型")
    @PostMapping("startModel")
    public ResultBody startModel(@RequestBody ModelTrain modelTrain, HttpServletRequest request) {
        String userid = request.getHeader("Userid");
        asyncCalcute(modelTrain, userid);
        return ResultBody.success();
    }

    /**
     * 异步调用模型
     * @param modelTrain 模型入参
     * @param userId 用户id
     */
    public void asyncCalcute(ModelTrain modelTrain, String userId) {
        ThreadUtil.execute(() -> {
            try {
                if (Boolean.TRUE.equals(modelTrain.getCheckMultiparameter())) {
                    // 多参数
                    List<String> params = StringUtil.split(modelTrain.getParamCode(), ',');
                    modelTrain.setParamCodes(params);

                    Object modelData = multiParameterService.multiparameter(modelTrain);
                    log.info("Websocket to web {}",WebsocketTopicEnum.DIAGNOSE_TRY.getCode() + "/" + userId);
                    simpMessagingTemplate.convertAndSend(WebsocketTopicEnum.DIAGNOSE_TRY.getCode() + "/" + userId, ResultBody.success(modelData));
                } else {
                    // 单参数
                    List<String> paramCodes = CollUtil.newArrayList();
                    paramCodes.add(modelTrain.getParamCode());
                    modelTrain.setParamCodes(paramCodes);
                    modelTrain.setSatelliteMid("");
                    ResultBody result = diagnoseService.startModel(modelTrain).body();
                    log.info("Websocket to web {}",WebsocketTopicEnum.DIAGNOSE_TRY.getCode() + "/" + userId);
                    simpMessagingTemplate.convertAndSend(WebsocketTopicEnum.DIAGNOSE_TRY.getCode() + "/" + userId, result);
                }

            } catch (Exception e) {
                e.printStackTrace();
                log.error("Websocket to web {}",WebsocketTopicEnum.DIAGNOSE_TRY.getCode() + "/" + userId,e);
                simpMessagingTemplate.convertAndSend(WebsocketTopicEnum.DIAGNOSE_TRY.getCode() + "/" + userId, ResultBody.failed(e.getMessage()));
            }
        });

    }


    /**
     * 获取图表数据
     * @param diagnoseRunDto 入参
     * @return ResultBody
     */
    @ApiOperation(value = "获取图表数据")
    @PostMapping("getDiagnoseEcharts")
    public ResultBody getDiagnoseEcharts(@RequestBody DiagnoseRunDto diagnoseRunDto) throws UnknownHostException {
        List<String> paramCodes = CollUtil.newArrayList();
        paramCodes.add(diagnoseRunDto.getParamCode());
        diagnoseRunDto.setModelName("lstm_Z16_17_S7_B12B0_6");
        ModelTrain modelTrain = ModelTrain.builder().paramCodes(paramCodes).modelName(diagnoseRunDto.getModelName())
                .satelliteCode("").satelliteMid("").build();
        return diagnoseService.startModel(modelTrain).body();
    }

    /**
     * 模型训练记录列表(分页)
     * @param modelDto 入参
     * @return ResultBody
     */
    @ApiOperation(value = "模型训练记录列表(分页)")
    @PostMapping("recordPage")
    public ResultBody recordPage(@RequestBody ModelDto modelDto) {
        PageInfo<ModelTrainRecord> pageList = modelTrainRecordService.pageList(modelDto);
        return ResultBody.success(pageList);
    }

    /**
     * 模型下拉列表
     * @param modelDto 入参
     * @return ResultBody
     */
    @ApiOperation(value = "模型下拉列表")
    @PostMapping("getModel")
    public ResultBody getModel(@RequestBody ModelDto modelDto) {
        return ResultBody.success(modelTrainRecordService.getModel(modelDto));
    }

    /**
     * 获取遥测数据(预处理后)
     * @param telemetryDto 遥测入参
     * @return ResultBody
     */
    @ApiOperation(value = "获取遥测数据(预处理后)")
    @PostMapping("getTelemetry")
    public ResultBody train(@RequestBody TelemetryDto telemetryDto) {
        Map<String, List<TelemetryData>> list = diagnoseService.selectTelemetryData(telemetryDto);
        //Map<String, List<TelemetryData>> result = diagnoseService.addDataToListMap(list);
        return ResultBody.success(list);
    }

    /**
     * 获取预测数据
     * @param telemetryDto 遥测入参
     * @return ResultBody
     */
    @ApiOperation(value = "获取预测数据")
    @PostMapping("getPredictData")
    public ResultBody getPredictData(@RequestBody TelemetryDto telemetryDto) {
        long startTime = telemetryDto.getStartTime().getTime();
        long endTime = telemetryDto.getEndTime().getTime();
        List<TelemetryData> listPredict = diagnoseService.selectTelemetryDataByTimeRange(telemetryDto);
        telemetryDto.setStartTime(new Date(startTime - (endTime-startTime)));
        telemetryDto.setEndTime(new Date(startTime));
        List<TelemetryData> listOrigin = diagnoseService.selectTelemetryDataByTimeRange(telemetryDto);
        listOrigin.add(listPredict.get(0));
        Map inputParam =new HashMap<String,Object>();
        inputParam.put("origin",listOrigin);
        inputParam.put("predict",listPredict);
        return ResultBody.success(inputParam);
    }

    /**
     * 对比预测数据
     * @param telemetryDto 遥测入参
     * @return ResultBody
     */
    @ApiOperation(value = "对比预测数据")
    @PostMapping("getContrastData")
    public ResultBody getContrastData(@RequestBody TelemetryDto telemetryDto) {
        List<TelemetryData> listPredict = diagnoseService.selectTelemetryDataByTimeRange(telemetryDto);
        List<TelemetryData> listOrigin = new ArrayList<>();
        for (TelemetryData data : listPredict){
            double value = Double.parseDouble(data.getValue());
            Random rand = new Random();
            double min = -2.5; // 最小值
            double max = 50.5; // 最大值
            double randomNum = min + (max - min) * rand.nextDouble();
            TelemetryData orgdata = BeanUtil.copyBean(data,TelemetryData.class);
            orgdata.setValue(String.valueOf(value+randomNum));
            listOrigin.add(orgdata);
        }
        Map inputParam =new HashMap<String,Object>();
        inputParam.put("origin",listOrigin);
        inputParam.put("predict",listPredict);
        return ResultBody.success(inputParam);
    }


    /**
     * 获取遥测数据(预处理后)
     * @param telemetryDto 遥测入参
     * @return ResultBody
     */
    @ApiOperation(value = "获取遥测数据(预处理后)")
    @PostMapping("getTelemetry1")
    public ResultBody train1(@RequestBody TelemetryDto telemetryDto) {
        Map<String, List<TelemetryData>> list = diagnoseService.selectTelemetryData(telemetryDto);
        return ResultBody.success(list);
    }

    /**
     * 查询数据接口
     * @param dataFeatureDto 遥测入参
     * @return ResultBody
     */
    @ApiOperation(value = "按时间段获取遥测数据")
    @PostMapping("getTelemetryData")
    public ResultBody getTelemetryData(@RequestBody DataFeatureDto dataFeatureDto) {
        List<TelemetryData> list = diagnoseService.getYcListByTimeRange(dataFeatureDto);
        return ResultBody.success(list);
    }

    /**
     * 获取模型准确度
     * @param modelTrainRecord 模型训练记录
     * @return ResultBody
     */
    @ApiOperation(value = "获取模型准确度")
    @PostMapping("getModelAccuracy")
    public ResultBody getModelAccuracy(@RequestBody ModelTrainRecord modelTrainRecord) {
        return modelTrainRecordService.selectInfo(modelTrainRecord).body();
    }


    /**
     * 事后诊断上线运行
     *
     * @param telemetryDto 遥测入参
     * @return ResultBody
     */
    @ApiOperation(value = "上线运行")
    @PostMapping("online")
    public ResultBody online(@Validated @ApiParam("入参") @RequestBody TelemetryDto telemetryDto) {
        return taskInfoService.online(telemetryDto);
    }

    /**
     * 根据子任务Id获取任务的异常点信息集合
     * @param taskId 子任务id
     * @return ResultBody
     */
    @ApiOperation(value = "根据子任务Id获取任务的异常点信息集合")
    @GetMapping("getTaskFaultDataList/{taskId}")
    public ResultBody getTaskFaultDataList(@PathVariable(value = "taskId") @ApiParam("子任务id") Long taskId) {
        List<TaskResultVo> result = diagnoseService.getTaskFaultDataList(taskId);
        return ResultBody.success(result);
    }

    /**
     * 根据子任务Id获取多参数信息集合(事后诊断-自动运行子任务查看)
     * @param taskChildId 子任务id
     * @return ResultBody
     */
    @ApiOperation(value = "根据子任务Id获取多参数信息集合(事后诊断-自动运行子任务查看)")
    @GetMapping("getMultipleParametersList/{taskId}")
    public ResultBody getMultipleParametersList(@PathVariable(value = "taskId") @ApiParam("子任务id") Long taskChildId) {
        Map<String, Object> result = multiParameterService.getMultipleParametersList(taskChildId);
        return ResultBody.success(result);
    }

    /**
     * 获取事后诊断算法名称列表根据类型
     * @param type 0单参数 1 多参数
     * @return ResultBody
     */
    @ApiOperation(value = "获取事后诊断算法名称列表根据类型")
    @GetMapping("getAlgorithmNameList/{type}")
    public ResultBody getAlgorithmNameList(@ApiParam("0单参数 1 多参数") @PathVariable(value = "type") Integer type) {
        return ResultBody.success(diagnoseService.getAlgorithmNameList(type));
    }


    /**
     * 查询参数在预处理中的信息
     * @param preprocessParamVo 预处理入参
     * @return ResultBody
     */
    @ApiOperation(value = "查询参数在预处理中的信息")
    @PostMapping("/preprocess/param")
    public ResultBody preprocessParam(@RequestBody PreprocessParamVo preprocessParamVo) {
        return taskParamInfoService.preprocessParam(preprocessParamVo).body();
    }


    /**
     * 事后诊断模型训练多短故障数据获取
     * @param failureInfoVo 故障信息入参
     * @return ResultBody
     */
    @ApiOperation(value = "事后诊断模型训练多短故障数据获取")
    @PostMapping("/failure")
    public ResultBody failureInfos(@RequestBody FailureInfoVo failureInfoVo) {
        return diagnoseService.failureInfos(failureInfoVo).body();
    }


    /**
     * 事后诊断自动运行查看子任务数据(单参)
     * @param taskFaultPredictDataVo  故障预测数据入参
     * @return ResultBody
     */
    @ApiOperation(value = "事后诊断自动运行查看子任务数据(单参)")
    @PostMapping("getTaskFaultPredictDataList")
    public ResultBody getTaskFaultPredictDataList(@RequestBody TaskFaultPredictDataVo taskFaultPredictDataVo) {
        return ResultBody.success(diagnoseService.getTaskFaultPredictDataList(taskFaultPredictDataVo));
    }


    /**
     * 事后诊断自动运行查看子任务数据(分页)
     * @param taskFaultPredictDataVo 故障预测数据入参
     * @return ResultBody
     */
    @ApiOperation(value = "事后诊断自动运行查看子任务数据(分页)")
    @PostMapping("getTaskFaultPredictPageList")
    public ResultBody getTaskFaultPredictPageList(@RequestBody TaskFaultPredictDataVo taskFaultPredictDataVo) {
        return ResultBody.success(diagnoseService.getTaskFaultPredictPageList(taskFaultPredictDataVo));
    }
}
